// File: jmcomic-core/src/main/java/dev/jukomu/jmcomic/option/DirRule.java
package dev.jukomu.jmcomic.option;

import dev.jukomu.common.util.Asserts;
import dev.jukomu.common.util.file.FileUtils;
import dev.jukomu.jmcomic.entity.DetailEntity;
import dev.jukomu.jmcomic.entity.JmAlbumDetail;
import dev.jukomu.jmcomic.entity.JmPhotoDetail;
import dev.jukomu.jmcomic.util.JmcomicText;
import lombok.Data;

import java.util.AbstractMap.SimpleEntry;
import java.util.*;
import java.util.function.BiFunction;

/**
 * 对应Python的DirRule，用于根据规则生成文件和目录路径。
 */
@Data
public class DirRule {

    public static final String RULE_BASE_DIR = "Bd";

    private final String baseDir; // 基础目录
    private final String ruleDsl; // 路径规则DSL
    private final List<Map.Entry<String, BiFunction<JmAlbumDetail, JmPhotoDetail, String>>> parserList; // 规则解析器列表

    /**
     * 构造函数。
     *
     * @param rule    路径规则DSL
     * @param baseDir 基础目录
     */
    public DirRule(String rule, String baseDir) {
        this.baseDir = JmcomicText.parseToAbspath(baseDir);
        this.ruleDsl = Objects.requireNonNull(rule, "Rule DSL cannot be null.");
        this.parserList = getRuleParserList(ruleDsl);
    }

    /**
     * 获取基础目录。
     *
     * @return 基础目录路径
     */
    public String getBaseDir() {
        return baseDir;
    }

    /**
     * 获取路径规则DSL。
     *
     * @return 路径规则DSL字符串
     */
    public String getRuleDsl() {
        return ruleDsl;
    }

    /**
     * 根据规则决定图片的保存目录。
     *
     * @param album 本子详情
     * @param photo 章节详情
     * @return 图片保存目录路径
     */
    public String decideImageSaveDir(JmAlbumDetail album, JmPhotoDetail photo) {
        return applyRuleToPath(album, photo, false);
    }

    /**
     * 根据规则决定本子的根目录。
     *
     * @param album 本子详情
     * @return 本子根目录路径
     */
    public String decideAlbumRootDir(JmAlbumDetail album) {
        return applyRuleToPath(album, null, true);
    }

    /**
     * 应用规则生成路径。
     *
     * @param album          本子详情
     * @param photo          章节详情
     * @param onlyAlbumRules 是否只应用本子相关的规则
     * @return 生成的路径
     */
    public String applyRuleToPath(JmAlbumDetail album, JmPhotoDetail photo, boolean onlyAlbumRules) {
        List<String> pathLs = new ArrayList<>();
        for (Map.Entry<String, BiFunction<JmAlbumDetail, JmPhotoDetail, String>> entry : parserList) {
            String rule = entry.getKey();
            BiFunction<JmAlbumDetail, JmPhotoDetail, String> parser = entry.getValue();

            if (onlyAlbumRules && !rule.equals(RULE_BASE_DIR) && !rule.startsWith("A")) {
                continue;
            }

            try {
                String path = parser.apply(album, photo);
                if (!rule.equals(RULE_BASE_DIR)) {
                    path = FileUtils.fixWinDirName(path).trim();
                }
                pathLs.add(path);
            } catch (Exception e) {
                dev.jukomu.jmcomic.config.JmModuleConfig.jmLog("dir_rule", String.format(
                        "路径规则\"%s\"的解析出错: %s, album=%s, photo=%s", rule, e.getMessage(), album, photo
                ));
                throw new RuntimeException("Error parsing dir rule: " + rule, e);
            }
        }
        return FileUtils.fixFilepath(String.join("/", pathLs));
    }

    /**
     * 解析路径规则DSL，生成解析器列表。
     */
    private List<Map.Entry<String, BiFunction<JmAlbumDetail, JmPhotoDetail, String>>> getRuleParserList(String ruleDsl) {
        List<String> ruleList = splitRuleDsl(ruleDsl);
        List<Map.Entry<String, BiFunction<JmAlbumDetail, JmPhotoDetail, String>>> parserList = new ArrayList<>();

        for (String rule : ruleList) {
            if (rule.equals(RULE_BASE_DIR)) {
                parserList.add(new SimpleEntry<>(rule, (a, p) -> this.baseDir));
                continue;
            }
            BiFunction<JmAlbumDetail, JmPhotoDetail, String> parser = getRuleParser(rule);
            parserList.add(new SimpleEntry<>(rule, parser));
        }
        return parserList;
    }

    /**
     * 分割路径规则DSL。
     */
    private List<String> splitRuleDsl(String ruleDsl) {
        List<String> ruleList = new ArrayList<>();
        if (ruleDsl.contains("/")) {
            ruleList.addAll(Arrays.asList(ruleDsl.split("/")));
        } else if (ruleDsl.contains("_")) {
            ruleList.addAll(Arrays.asList(ruleDsl.split("_")));
        } else {
            ruleList.add(ruleDsl);
        }

        if (ruleList.isEmpty() || !ruleList.get(0).equals(RULE_BASE_DIR)) {
            ruleList.add(0, RULE_BASE_DIR);
        }
        return ruleList;
    }

    /**
     * 根据规则获取解析器。
     */
    private static BiFunction<JmAlbumDetail, JmPhotoDetail, String> getRuleParser(String rule) {
        if (rule.contains("{")) {
            return (album, photo) -> parseFStringRule(album, photo, rule);
        }
        if (rule.startsWith("A") || rule.startsWith("P")) {
            return (album, photo) -> parseDetailRule(album, photo, rule);
        }
        Asserts.requireTrue(false, "不支持的rule配置: \"" + rule + "\"");
        return null; // unreachable
    }

    /**
     * 解析F-String格式的规则。
     */
    private static String parseFStringRule(JmAlbumDetail album, JmPhotoDetail photo, String rule) {
        Map<String, Object> properties = new HashMap<>();
        if (album != null) {
            properties.putAll(album.getPropertiesMap());
        }
        if (photo != null) {
            properties.putAll(photo.getPropertiesMap());
        }

        // Java中没有直接的f-string，需要手动替换
        String result = rule;
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            result = result.replace("{" + entry.getKey() + "}", String.valueOf(entry.getValue()));
        }
        return result;
    }

    /**
     * 解析DetailEntity属性的规则。
     */
    private static String parseDetailRule(JmAlbumDetail album, JmPhotoDetail photo, String rule) {
        DetailEntity<?> detail = rule.startsWith("A") ? album : photo;
        Asserts.requireTrue(detail != null, "DetailEntity cannot be null for rule: " + rule);
        return DetailEntity.getDirname(detail, rule.substring(1));
    }

    /**
     * 根据规则生成文件名。
     */
    public static String applyRuleToFilename(JmAlbumDetail album, JmPhotoDetail photo, String rule) {
        if (album == null && photo != null) {
            album = photo.getFromAlbum();
        }
        String filename = getRuleParser(rule).apply(album, photo);
        return FileUtils.fixWinDirName(filename).trim();
    }
}